package com.jsbs.iam.ident.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jsbs.iam.common.core.dto.Result;
import com.jsbs.iam.common.core.param.AccountInfoDto;
import com.jsbs.iam.common.core.utils.Json;
import com.jsbs.iam.common.core.utils.PageHelperTool;
import com.jsbs.iam.common.core.utils.StringUtils;
import com.jsbs.iam.common.redis.service.RedisUtils;
import com.jsbs.iam.ident.constant.RedisConstants;
import com.jsbs.iam.ident.dto.IamUpdateAccountStatus;
import com.jsbs.iam.ident.dto.IamUserInfoDto;
import com.jsbs.iam.ident.dto.IdentityAccountDto;
import com.jsbs.iam.ident.entity.IamAccountBase;
import com.jsbs.iam.ident.feign.IamIdentAuthFeignInter;
import com.jsbs.iam.ident.feign.IdentFeignInter;
import com.jsbs.iam.ident.mapper.IamUpdateUserLogMapper;
import com.jsbs.iam.ident.mapper.IdentityMapper;
import com.jsbs.iam.ident.param.IamApplyDetaislParam;
import com.jsbs.iam.ident.param.IamQueryUserByOrgParam;
import com.jsbs.iam.ident.param.IamUserInfoParam;
import com.jsbs.iam.ident.service.IdentityService;
import com.jsbs.iam.ident.vo.AdminRoleVo;
import com.jsbs.iam.ident.vo.IamAccountBaseVo;
import com.jsbs.iam.ident.vo.IamAdminRoleOrgInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @ClassName: AccountServiceImpl
 * @Author: tenghuawei
 * @Date: 2022/12/5 15:21
 * @Description:
 */

@Slf4j
@Service
@SuppressWarnings("all")
public class IdentityServiceImpl implements IdentityService {

    @Autowired
    private IdentityMapper identityMapper;

    @Resource
    private RedissonClient redissonClient;

    @Autowired
    IdentFeignInter identFeignInter;

    @Autowired
    private IamUpdateUserLogMapper iamUpdateUserLogMapper;

    @Autowired
    private IamIdentAuthFeignInter iamIdentAuthFeignInter;

    /**
     * 手机号长度
     */
    private static final Integer length = 11;

    /**
     * 手机号第一位
     */
    private static final Integer PH = 1;

    @Value("${_env.name}")
    private String envName;

    /**
     * 账户中心——账号列表分页条件查询（重置按钮可以复用）
     * @return PageInfo
     */
    @Override
    public PageInfo queryAccountList(IdentityAccountDto identityAccountDto) {
        // 初始化分页数据
        identityAccountDto.init();
        PageHelper.startPage(identityAccountDto.getCurrPage(), identityAccountDto.getPageSize());
        List<IamAccountBaseVo> accountList = identityMapper.queryAccountList(identityAccountDto);
        if(CollectionUtils.isEmpty(accountList)){
            log.warn("Route=>IdentityServiceImpl method=>queryAccountList 账号列表条件查询入参为：{}，未找到相关数据，建议您更换筛选条件！", Json.toJsonString(identityAccountDto));
        }
        PageInfo<IamAccountBaseVo> pageInfo0= new PageInfo<>(accountList);
        PageInfo<IamAccountBaseVo> pageInfo = PageHelperTool.initPageInfoObj(identityAccountDto.getCurrPage(), (int) pageInfo0.getTotal(),
                                                                    identityAccountDto.getPageSize(), pageInfo0);
        return pageInfo0;
    }

    /**
     * 功能描述：账户中心——账号列表分页条件查询（普管）
     * @param identityAccountDto
     * @return PageInfo
     */
    @Override
    public PageInfo<IamAccountBaseVo> queryAccountListRange(IdentityAccountDto identityAccountDto) {
        // 初始化分页数据
        identityAccountDto.init();
        // admin范围内全量账号数据
        List<IamAccountBaseVo> accountList = identityMapper.queryAccountList(identityAccountDto);
        // 当前登录角色的应用范围 + 组织范围
        Result result = identFeignInter.queryAdminRole(identityAccountDto.getAdminRoleCode(), identityAccountDto.getCompanyCode());
        if(null==result.getData()){
            log.warn("Route=>IdentityServiceImpl method=>queryAccountListRange 账号列表分页条件查询（普管）查询数据为空！");
            return new PageInfo<>();
        }
        AdminRoleVo adminRoleVo = JSON.parseObject(JSON.toJSONString(result.getData()), AdminRoleVo.class);

        // 当前登录普管角色的组织范围
        List<IamAdminRoleOrgInfoVo> roleVoOrgRoleList = JSON.parseArray(JSON.toJSONString(adminRoleVo.getOrgRoleList()),IamAdminRoleOrgInfoVo.class);
        // usercode数据封装
        List<String> userCodes = new ArrayList<>();

            // todo 组织范围中查
            roleVoOrgRoleList.forEach(org->{
                IamQueryUserByOrgParam iamQueryUserByOrgParam = new IamQueryUserByOrgParam();
                iamQueryUserByOrgParam.setOrgCode(org.getOrgCode());
                iamQueryUserByOrgParam.setCompanyCode(identityAccountDto.getCompanyCode());
                Result result1 = identFeignInter.queryAccountListRange(iamQueryUserByOrgParam);
                List<String> codes = JSON.parseArray(JSON.toJSONString(result1.getData()),String.class);
                userCodes.addAll(codes);
            });

        List<IamAccountBaseVo> accounts = new ArrayList<>();
        for (String userCode : userCodes){
            log.info("-------------交集 intersection-------------");
            List<IamAccountBaseVo> accountIntersection = accountList.stream().
                    filter(item -> Objects.nonNull(item) && item.getUserCode().equals(userCode)).collect(Collectors.toList());
            accounts.addAll(accountIntersection);
        }

        PageInfo<IamAccountBaseVo> pageInfo0= new PageInfo<>(accounts);
        PageInfo<IamAccountBaseVo> pageInfo = PageHelperTool.initPageInfoObj(identityAccountDto.getCurrPage(), (int) pageInfo0.getTotal(),
                identityAccountDto.getPageSize(), pageInfo0);
        return pageInfo;
    }

    /**
     * 账户中心——账号导出查询（导出不需要分页）
     * @return
     */
    @Override
    public List<IamAccountBaseVo> queryExcelAccountList(IdentityAccountDto identityAccountDto) {
        List<IamAccountBaseVo> accountList = identityMapper.queryAccountList(identityAccountDto);
        if(CollectionUtils.isEmpty(accountList)){
            log.warn("Route=>IdentityServiceImpl method=>queryExcelAccountList 账号导出查询入参为：{}，未找到相关数据，建议您更换筛选条件！", Json.toJsonString(identityAccountDto));
        }
        return accountList;
    }

    /**
     * 功能描述：根据用户编码，批量删除账号
     * @param userCode
     * @return int
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchDeleteByUserCode(List<String> userCode,String companyCode){
        try{
            Result result = identFeignInter.batchDeleteByUserCode(userCode);
            if(0!=result.getCode()){
                return 0;
            }
            return identityMapper.batchDeleteByUserCode(userCode,companyCode);
        }catch (Exception e){
            log.error("Route=>IdentityServiceImpl method=>batchDeleteByUserCode 根据用户id批量删除异常:{}", e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return 0;
        }
    }
    /**
     * 启用或停用账号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateAccountStatus(IamUpdateAccountStatus iamUpdateAccountStatus) {
        return identityMapper.updateAccountStatus(iamUpdateAccountStatus);
    }

    /**
     * 重置密码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resetPassword(IamUpdateAccountStatus iamUpdateAccountStatus) {
        try {
            iamUpdateAccountStatus.getUserCode().stream().forEach(code ->{
            IamAccountBase iamAccountBase = identityMapper.queryByUserCode(code,iamUpdateAccountStatus.getCompanyCode());
            //密码明文->密文
            String MD5Pass = DigestUtils.md5DigestAsHex(iamAccountBase.getPhone().substring(iamAccountBase.getPhone().length() - 6).getBytes());
            int i = identityMapper.resetPassword(code, MD5Pass,iamUpdateAccountStatus.getCompanyCode());
            if(0 == i) {
                throw new RuntimeException("重置密码失败！");
            }
        });
            log.info("Route=>IdentityServiceImpl method=>resetPassword 重置密码成功");
            return true;
        }catch(Exception e){
            log.error("Route=>IdentityServiceImpl method=>resetPassword 重置密码异常:{}", e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 功能描述：自动创建账户信息
     * @param accountInfoDtos
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> accountCreate(List<AccountInfoDto> accountInfoDtos) {
        // 返回结果集
        List<String> accountResults = new ArrayList<>();
        // 判空
        if (CollectionUtils.isEmpty(accountInfoDtos)) {
            return accountResults;
        }

        // 新增用户生成用户ID时加锁，失效时间10秒钟
        RLock lock = redissonClient.getLock(RedisConstants.ACCOUNT_INFO_CODE_LOCK);
        Long code = null;
        try {
            // 5分钟内重试获取锁，10秒后释放
            boolean isLocked = lock.tryLock(5 * 60 * 1000L, 10 * 1000L, TimeUnit.MILLISECONDS);
            // 获取到锁后，进行自增
            if (!isLocked) {
                log.warn("Route=>IdentityServiceImpl method=>accountCreate 自动创建账户信息加锁生成账户ID失败:{}", Json.toJsonString(accountInfoDtos));
                throw new Exception();
            }
            // 加锁成功后获取当前缓存值
            String value = RedisUtils.get(envName + RedisConstants.ACCOUNT_INFO_CODE);
            // 若为空则从数据库取当前最大值，并加新增用户数量入缓存
            if (StringUtils.isEmpty(value)) {
                code = Long.valueOf(identityMapper.getMaxAccountId()) + 1;
            } else {
                // 当前缓存获取成功，并加新增用户数量入缓存
                code = Long.parseLong(value) + 1;
            }
            // 将最新值更新到缓存中
            RedisUtils.set(envName + RedisConstants.ACCOUNT_INFO_CODE, String.valueOf(code + accountInfoDtos.size() - 1));
        } catch (Exception e) {
            log.error("Route=>IdentityServiceImpl method=>accountCreate 自动创建账户信息加锁生成账户ID失败:{}, e:{}", Json.toJsonString(accountInfoDtos), e);
            throw new RuntimeException("创建账户信息生成账户ID异常");
        } finally {
            // 手动解锁
            if (lock.isLocked()) {
                lock.unlock();
            }
        }

        // 账户列表
        List<IamAccountBase> accountBases = new ArrayList<>();
        AtomicReference<Long> finalCode = new AtomicReference<>(code);
        // 封装数据
        accountInfoDtos.forEach(item -> {
            IamAccountBase accountBase = new IamAccountBase();
            BeanUtils.copyProperties(item, accountBase);
            // 账户ID
            accountBase.setAccountId("A" + String.format("%09d", finalCode.getAndSet(finalCode.get() + 1)));
            accountBase.setMode(1);
            accountBase.setStatus(1);
            if(item.getAccountStatus()==null){
                accountBase.setAccountStatus(1);
            }
            //密码明文->密文
            String MD5Pass = DigestUtils.md5DigestAsHex(item.getPhone().substring(item.getPhone().length() - 6).getBytes());
            accountBase.setPassword(MD5Pass);
            accountBases.add(accountBase);
            accountResults.add(item.getUserCode());
        });
        if (CollectionUtils.isNotEmpty(accountBases)) {
            // 批量入库
            identityMapper.batchInsert(accountBases);
        }
        return accountResults;
    }

    @Override
    public IamAccountBase getAccountPassword(IamAccountBase iamAccountBase) {
        //根据用户id或用户手机号验证用户信息
        IamAccountBase accountPassword = identityMapper.getAccountPassword(iamAccountBase);
        return accountPassword;
    }

    /**
     * 导入用户数据
     * @param accountList 用户数据列表
     * @param companyCode 归属租户
     * @param userName 操作用户
     * @return String
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String accountImport(List<IamAccountBaseVo> accountList, String companyCode, String userName) {
        if (CollectionUtils.isEmpty(accountList)) {
            throw new RuntimeException("导入的excel文件中，账户数据不能为空！");
        }
        //对导入的excel文件校验：根据手机+组织类型编码，进行数据去重
        ArrayList<IamAccountBaseVo> collect = accountList.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getPhone() + ";" + o.getOrgTypeCode()))), ArrayList::new)
        );
        if(collect.size() < accountList.size()) {
            throw new RuntimeException(String.format("共：%d 条数据重复，请筛选excel文件中的重复数据后，重新导入!",accountList.size()-collect.size()));
        }

        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        //封装结果集
        List<AccountInfoDto> accountInfoDtos = new ArrayList<AccountInfoDto>();
        //CAS
        AtomicInteger atomicInteger = new AtomicInteger(0);
        for (IamAccountBaseVo account : accountList) {
            try {
                atomicInteger.getAndIncrement();
                //封装请求参数类
                IamUserInfoParam iamUserInfoParam = new IamUserInfoParam();
                if (StringUtils.isBlank(account.getPhone())) {
                    throw new RuntimeException(String.format("第 %d 行数据的手机号不可为空!",atomicInteger.get()));
                }
                if (StringUtils.isBlank(account.getOrgTypeCode())) {
                    throw new RuntimeException(String.format("第 %d 行数据的组织类型编码不可为空!",atomicInteger.get()));
                }
                // todo 校验手机号 thw
                //封装openfeign的查询参数
                iamUserInfoParam.setPhone(account.getPhone());
                iamUserInfoParam.setOrgTypeCode(account.getOrgTypeCode());
                iamUserInfoParam.setCompanyCode(companyCode);
                //姓名若不为空，需校验其正确性
                if (StringUtils.isNotBlank(account.getName())) {
                    log.info("Route=>IdentityServiceImpl method=>accountImport 该行数据姓名不为空！");
                    iamUserInfoParam.setName(account.getName());
                }
                //先校验是否已经开通了该账号
                int countAccountBase = identityMapper.countAccountBase(iamUserInfoParam);
                if(0<countAccountBase){
                    throw new RuntimeException(String.format("第 %d 行数据:该账号已开通，无需重复开通!",atomicInteger.get()));
                }
                //根据手机+组织类型编码+租户编码，查询用户中心是否有该用户
                Object data = identFeignInter.getUserInfoByAny(iamUserInfoParam).getData();
                if(Objects.isNull(data)){
                    throw new RuntimeException(String.format("第 %d 行数据：用户中心无此账号信息，该账号导入失败!",atomicInteger.get()));
                }
                IamUserInfoDto iamUserInfoDto = JSON.parseObject(JSON.toJSONString(data), IamUserInfoDto.class);

                if (ObjectUtils.isNotEmpty(iamUserInfoDto)) {
                    if (StringUtils.isNotBlank(iamUserInfoParam.getName())) {
                        // 取用户中心的数据库，核实用户姓名是否正确
                        if (!StringUtils.equalsIgnoreCase(iamUserInfoDto.getName(), iamUserInfoParam.getName())) {
                            throw new RuntimeException(String.format("第 %d 行数据:该账号姓名不正确!",atomicInteger.get()));
                        }
                    }
                    AccountInfoDto accountInfoDto = new AccountInfoDto();
                    BeanUtils.copyProperties(iamUserInfoDto, accountInfoDto);
                    //归属租户
                    accountInfoDto.setCompanyCode(companyCode);
                    //操作人编码
                    accountInfoDto.setCreatePerson(userName);
                    accountInfoDto.setAccountStatus(account.getAccountStatus());
                    log.info("Route=>IdentityServiceImpl method=>accountImport 入账的accountInfoDto为：{}", accountInfoDto);
                    //封账
                    accountInfoDtos.add(accountInfoDto);

                    successNum++;
                    successMsg.append("第"+successNum + "条账号信息：" + account.getName() + " 导入成功！");
                } else {
                    failureNum++;
                    failureMsg.append("第"+failureNum + "条账号信息：" + account.getName() + "-用户中心不存在！");
                }
            } catch (Exception e) {
                failureNum++;
                String msg = "第"+failureNum + "条账号信息：" + account.getName() + " 导入异常：";
                failureMsg.append(msg+e.getMessage());
                log.error("Route=>IdentityServiceImpl method=>accountImport 导入失败的消息提示：{},异常：{}", msg, e);
            }
        }
        if (failureNum > 0) {
            failureMsg.insert(0, "很抱歉，导入失败！共 " + failureNum + " 条数据格式不正确，错误如下：");
            throw new RuntimeException(failureMsg.toString());
        }
        else {
            List<String> userCode = accountCreate(accountInfoDtos);
            log.info("Route=>IdentityServiceImpl method=>accountImport 账号导入的所有用户id为：{}", userCode);
            successMsg.insert(0, "恭喜您，数据已全部导入成功！本次导入共 " + successNum + " 条数据，账号导入的所有用户id为："+userCode+"——导入详情：");
        }
        return successMsg.toString();
    }

    /**
     * 功能描述：查看账户的应用详情
     * @param iamApplyDetaislParam
     * @return Result
     */
    @Override
    public Result getApplicationDetails(IamApplyDetaislParam iamApplyDetaislParam) {
        Result result = iamIdentAuthFeignInter.getApplicationDetails(iamApplyDetaislParam);
        return result;
    }

    @Override
    public List<IamAccountBaseVo> getAccountBase(String accountId, String companyCode) {
        return identityMapper.getAccountBase(accountId,companyCode);
    }

    /**
     * 密码修改
     * @param employeeCode
     * @param newPassword
     * @param oldPassword
     * @return
     */
    @Override
    public int updatePassword(String employeeCode, String newPassword, String oldPassword) throws Exception {
        if (StringUtils.isBlank(employeeCode)) {
            throw new Exception("用户名不能为空");
        }
        if (StringUtils.isBlank(newPassword)) {
            throw new Exception("新密码不能为空");
        }
        if (StringUtils.isBlank(oldPassword)) {
            throw new Exception("原密码不能为空");
        }

        IamAccountBase iamAccountBase = new IamAccountBase();
        String trim = employeeCode.trim();
        //判断用户名是否全部为数字并且是是十一位，如果是说明是手机号登录
        if (org.apache.commons.lang3.StringUtils.isNumeric(trim) && trim.length() == length){
            //判断第一个值是否为1
            String sOne = employeeCode.substring(0,1);
            if (!sOne.equals(PH)) {
                throw new Exception("手机号格式错误");
            }
            // 手机号登录
            iamAccountBase.setPhone(employeeCode);
        } else {
            // 用户编码登录
            iamAccountBase.setEmployeeCode(employeeCode);
        }
//        iamAccountBase.setPassword(DigestUtils.md5DigestAsHex(oldPassword.getBytes()));
        iamAccountBase.setPassword(oldPassword);
        IamAccountBase accountPassword = this.getAccountPassword(iamAccountBase);
        if (accountPassword == null) {
            throw new Exception("原密码错误");
        }
        if (oldPassword.equals(newPassword)) {
            throw new Exception("新密码不能同原密码一样");
        }
        int i= 0;
        if (accountPassword != null) {
            iamAccountBase.setPassword(newPassword);
            i= identityMapper.updatePassword(iamAccountBase.getEmployeeCode(),iamAccountBase.getPassword());
        }
        return i;
    }

    @Override
    public void accountFail(List<String> userCodeList,String companyCode){
        if (CollectionUtils.isEmpty(userCodeList)) {
            return;
        }
        // 批量置为失效
        identityMapper.batchDeleteByUserCode(userCodeList,companyCode);
    }
}
